package com.njworkorder.Controller;


import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import com.njworkorder.DTO.ApprovalMcpAddDto;
import com.njworkorder.Entity.*;
import com.njworkorder.Mapper.PlanMeteringMapper;
import com.njworkorder.Service.ApprovalMcpService;
import com.njworkorder.Service.AssignmentService;
import com.njworkorder.Service.PlanMonthlyAllocationService;
import com.njworkorder.Service.PlanService;
import com.njworkorder.Utils.Result;
import com.njworkorder.Utils.ResultUtil;
import com.njworkorder.VO.ApprovalMcpVo;
import com.njworkorder.VO.FinancialProgressVo;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.web.bind.annotation.*;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.time.LocalDate;
import java.util.*;
import java.util.concurrent.CompletableFuture;

import static com.baomidou.mybatisplus.extension.ddl.DdlScriptErrorHandler.PrintlnLogErrorHandler.log;

@Tag(name = "支付管理", description = "资金进度管理相关接口")
@RestController
@RequestMapping("/approvalMcp")
public class ApprovalMcpController {

    private final ApprovalMcpService approvalMcpService;
    private final PlanService planService;
    private final AssignmentService assignmentService;
    private final PlanMonthlyAllocationService planMonthlyAllocationService;
    private final PlanMeteringMapper planMeteringMapper;
    public ApprovalMcpController(ApprovalMcpService approvalMcpService,PlanService planService,
                                 AssignmentService assignmentService,
                                 PlanMonthlyAllocationService planMonthlyAllocationService,
                                 PlanMeteringMapper planMeteringMapper) {
        this.approvalMcpService = approvalMcpService;
        this.planService = planService;
        this.assignmentService = assignmentService;
        this.planMonthlyAllocationService = planMonthlyAllocationService;
        this.planMeteringMapper = planMeteringMapper;
    }

    @Operation(summary = "添加季度日常养护费用支付审批信息")
    @PostMapping("/addInfo")
    public Result<String> addInfo(@RequestBody ApprovalMcpAddDto approvalMcpAddDto, @RequestHeader("Create-Ap-Id") String createApId){

        ApprovalMcp approvalMcp = new ApprovalMcp();
        BeanUtils.copyProperties(approvalMcpAddDto,approvalMcp);
        approvalMcp.setCreateApId(createApId);

        boolean save = approvalMcpService.save(approvalMcp);
        if(save){
            return ResultUtil.success("添加成功");
        }
        return ResultUtil.fail("添加失败");
    }

    @Operation(summary = "根据id更新季度日常养护费用支付审批信息")
    @PostMapping("/updateInfoById/{id}")
    public Result<String> updateInfoById(@RequestBody ApprovalMcpAddDto approvalMcpAddDto, @PathVariable("id") String id){
        ApprovalMcp approvalMcp = new ApprovalMcp();
        BeanUtils.copyProperties(approvalMcpAddDto,approvalMcp);
        approvalMcp.setId(id);

        boolean b = approvalMcpService.updateById(approvalMcp);
        if(b){
            return ResultUtil.success("更新成功");
        }
        return ResultUtil.fail("更新失败");
    }

    @Operation(summary = "根据id查询季度日常养护费用支付审批信息")
    @GetMapping("/getInfoById")
    public Result<ApprovalMcpVo> getInfoById(@RequestParam("id") String id){

        MPJLambdaWrapper<ApprovalMcp> wrapper = new MPJLambdaWrapper<ApprovalMcp>()
                .selectAll(ApprovalMcp.class)
                .selectAs(AdministrativePersonnel::getName, ApprovalMcpVo::getCreateApName)
                .leftJoin(AdministrativePersonnel.class,AdministrativePersonnel::getId,ApprovalMcp::getCreateApId)
                .eq(ApprovalMcp::getId,id);

        ApprovalMcpVo approvalMcpVo = approvalMcpService.selectJoinOne(ApprovalMcpVo.class, wrapper);
        return ResultUtil.success(approvalMcpVo);
    }

    @Operation(summary = "根据id删除季度日常养护费用支付审批信息")
    @GetMapping("/deleteInfoById")
    public Result<String> deleteInfoById(@RequestParam("id") String id){
        boolean b = approvalMcpService.removeById(id);
        if(b){
            return ResultUtil.success("删除成功");
        }
        return ResultUtil.success("删除失败");
    }


    @Operation(summary = "获取列表季度日常养护费用支付审批信息")
    @GetMapping("/getList")
    public Result<Page<ApprovalMcpVo>> getList(@RequestParam("pageIndex") int pageIndex, @RequestParam("pageSize") int pageSize,
                                               @RequestParam(value = "assessmentScore",required = false) String assessmentScore,
                                               @RequestParam(value = "yearBtpf",required = false) String yearBtpf){

        MPJLambdaWrapper<ApprovalMcp> wrapper = new MPJLambdaWrapper<ApprovalMcp>()
                .selectAll(ApprovalMcp.class)

                .leftJoin(AdministrativePersonnel.class,AdministrativePersonnel::getId,ApprovalMcp::getCreateApId,ext ->
                        ext.selectAs(AdministrativePersonnel::getName, ApprovalMcpVo::getCreateApName))
                .leftJoin(AdministrativePersonnel.class,AdministrativePersonnel::getId,ApprovalMcp::getMaintainOperator,ext ->
                        ext.selectAs(AdministrativePersonnel::getName, ApprovalMcpVo::getMaintainOperatorName))
                .leftJoin(AdministrativePersonnel.class,AdministrativePersonnel::getId,ApprovalMcp::getMaintainReviewer,ext ->
                        ext.selectAs(AdministrativePersonnel::getName, ApprovalMcpVo::getMaintainReviewerName))
                .leftJoin(AdministrativePersonnel.class,AdministrativePersonnel::getId,ApprovalMcp::getMaintainDirector,ext ->
                        ext.selectAs(AdministrativePersonnel::getName, ApprovalMcpVo::getMaintainDirectorName))
                .leftJoin(AdministrativePersonnel.class,AdministrativePersonnel::getId,ApprovalMcp::getJhOperator,ext ->
                        ext.selectAs(AdministrativePersonnel::getName, ApprovalMcpVo::getJhOperatorName))
                .leftJoin(AdministrativePersonnel.class,AdministrativePersonnel::getId,ApprovalMcp::getJhEngineeringDptDirector,ext ->
                        ext.selectAs(AdministrativePersonnel::getName, ApprovalMcpVo::getJhEngineeringDptDirectorName))
                .leftJoin(AdministrativePersonnel.class,AdministrativePersonnel::getId,ApprovalMcp::getJhPfdDirector,ext ->
                        ext.selectAs(AdministrativePersonnel::getName, ApprovalMcpVo::getJhPfdDirectorName))

                .eq(StringUtils.isNotEmpty(assessmentScore),ApprovalMcp::getAssessmentScore,assessmentScore)
                .eq(StringUtils.isNotEmpty(yearBtpf),ApprovalMcp::getYearBtpf,yearBtpf)
                .apply("1=1");

        Page<ApprovalMcpVo> approvalMcpVoPage = approvalMcpService.selectJoinListPage(new Page<>(pageIndex, pageSize), ApprovalMcpVo.class, wrapper);
        return ResultUtil.success(approvalMcpVoPage);

    }


    @Operation(summary = "资金进度管理")
    @GetMapping("/getFinancialProgress")
    public Result<List<FinancialProgressVo>> getFinancialProgress(@RequestParam("year") int year,
                                                                  @RequestParam(value = "planId",required = false) String planId){

        String[] strings = generateMonthsForYear(year);
        DecimalFormat df = new DecimalFormat("#.##");

        CompletableFuture<List<BillOfQuantities>> billOfQuantities = assignmentService.getBillOfQuantities(year);
        CompletableFuture<List<PlanMetering>> planMetering = planService.getPlanMetering(year);

        CompletableFuture.allOf(billOfQuantities,planMetering).join();

        List<BillOfQuantities> billOfQuantitiesList = billOfQuantities.join();
        List<PlanMetering> planMeteringList = planMetering.join();

        List<PlanMetering> Year = new ArrayList<>();
        if(!planMeteringList.isEmpty()){
            Year = planMeteringList.stream().filter(f -> f.getMeteringInventoryId().equals("1")).toList();
        }

        double ye = 0;
        double mon = 0;
        if(!Year.isEmpty()){
            BigDecimal roundedValueY = Year.getFirst().getUnitPrice() == null ? new BigDecimal("0") :Year.getFirst().getUnitPrice();
            roundedValueY = roundedValueY.setScale(2, RoundingMode.HALF_UP);
            ye =  roundedValueY.doubleValue();
        }
        if(!Year.isEmpty()){
            BigDecimal roundedValueM = Year.getFirst().getUnitPrice() == null ? new BigDecimal("0") :Year.getFirst().getUnitPrice().divide(new BigDecimal("12"), 4, RoundingMode.HALF_UP);
            roundedValueM = roundedValueM.setScale(2, RoundingMode.HALF_UP);
            mon = roundedValueM.doubleValue();
        }

        List<FinancialProgressVo> list = new ArrayList<>();
        FinancialProgressVo financialProgressVo;

        for (int i = strings.length -1; i >= 0; i--) {
            financialProgressVo = new FinancialProgressVo();
            financialProgressVo.setDate(strings[i]);
            financialProgressVo.setMonthlyPlanCost(mon);
            financialProgressVo.setAnnualPlanCost(ye);

            int index = i;
            List<BillOfQuantities> billOfQuantitiesList1 = billOfQuantitiesList.stream().filter(f -> f.getRemark().equals(strings[index])).toList();
            if(!billOfQuantitiesList1.isEmpty()){
                String format = df.format(billOfQuantitiesList1.stream().mapToDouble(m -> m.getNums() * Double.parseDouble(m.getUnitPrice())).sum());
                financialProgressVo.setMonthlyUsedCost(Double.parseDouble(format));
            }

            if(financialProgressVo.getMonthlyPlanCost() > 0){
                financialProgressVo.setMonthlyFP(df.format((financialProgressVo.getMonthlyUsedCost() / financialProgressVo.getMonthlyPlanCost()) * 100) + "%");
            }

            list.add(financialProgressVo);
        }


        double AccumulateUsedCost = 0;
        for (FinancialProgressVo progressVo : list) {
            AccumulateUsedCost += progressVo.getMonthlyUsedCost();
            progressVo.setAccumulateUsedCost(AccumulateUsedCost);
            if (progressVo.getMonthlyPlanCost() > 0) {
                progressVo.setAnnualFP(df.format((progressVo.getAccumulateUsedCost() / progressVo.getAnnualPlanCost()) * 100) + "%");
            }
        }

        return ResultUtil.success(list.stream().sorted(Comparator.comparing(FinancialProgressVo::getDate).reversed()).toList());
    }

    public static String[] generateMonthsForYear(int year) {

        int currentYear = LocalDate.now().getYear();
        int currentMonth = LocalDate.now().getMonthValue();

        if (year > currentYear) {
            throw new RuntimeException("输入的年份在未来");
        }

        // 计算需要生成的月份数量
        int monthCount = (year == currentYear) ? currentMonth : 12;

        String[] months = new String[monthCount];
        for (int i = 0; i < monthCount; i++) {
            // 从当前月份开始倒序生成
            int month = (year == currentYear) ? currentMonth - i : 12 - i;
            String monthStr = (month < 10) ? "0" + month : String.valueOf(month);
            months[i] = year + "-" + monthStr;
        }
        return months;
    }



    @Operation(summary = "资金进度管理")
    @GetMapping("/getFinancialProgressT")
    public Result<List<FinancialProgressVo>> getFinancialProgressT(@RequestParam(value = "planId",required = false) String planId) {
        DecimalFormat df = new DecimalFormat("#.##");
        // 获取Calendar实例
        Calendar calendar = Calendar.getInstance();
        // 获取当前月份 (注意：Calendar中的月份是从0开始的，所以需要+1)
        int month = calendar.get(Calendar.MONTH) + 1;

        String[] strings = generateMonthsForYear(calendar.get(Calendar.YEAR));
        //int year = calendar.get(Calendar.YEAR);

        MPJLambdaWrapper<PlanMetering> wrapperPM = new MPJLambdaWrapper<PlanMetering>()
                .selectAs("SUM(t.unit_price * t2.num)",PlanMetering::getSpecialUnitPrice)
                .selectAs("MONTH(t2.is_date)",PlanMetering::getIsSpecial)
                .selectAs("YEAR(t2.is_date)",PlanMetering::getUnit)
                .leftJoin(MeteringInventory.class,MeteringInventory::getId,PlanMetering::getMeteringInventoryId)
                .leftJoin(MeteringUse.class,MeteringUse::getProjectCode,MeteringInventory::getProjectNo)
                .ne(MeteringUse::getPricingType,3)
                .eq(StringUtils.isNotEmpty(planId),PlanMetering::getPlanId,planId)
                .groupBy("YEAR(t2.is_date)","MONTH(t2.is_date)");
        List<PlanMetering> planMeterings = planMeteringMapper.selectJoinList(PlanMetering.class, wrapperPM);


        MPJLambdaWrapper<PlanMonthlyAllocation> wrapper = new MPJLambdaWrapper<PlanMonthlyAllocation>()
                .selectAll(PlanMonthlyAllocation.class)
                .leftJoin(Plan.class,Plan::getId,PlanMonthlyAllocation::getPlanId)
                .eq(PlanMonthlyAllocation::getPlanId, planId);
        List<PlanMonthlyAllocation> list = planMonthlyAllocationService.selectJoinList(PlanMonthlyAllocation.class, wrapper);


        BigDecimal sum = list.stream()
                .map(PlanMonthlyAllocation::getOutputValue)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        double annualPlannedExpenses = sum.doubleValue() * 10000;//计划总费用，单位元

        List<FinancialProgressVo> listRet = new ArrayList<>();
        FinancialProgressVo financialProgressVo;

        for (int i = strings.length -1; i >= 0; i--) {
            financialProgressVo = new FinancialProgressVo();
            financialProgressVo.setDate(strings[i]);

            double MONTHSum = 0;//本月计划费用
            for (PlanMonthlyAllocation item : list) {
                String monthRatioByReflection = getMonthRatioByReflection(item, month-i);
                if(StringUtils.isNotEmpty(monthRatioByReflection)){
                    double a = item.getOutputValue().doubleValue() * 10000;
                    MONTHSum += a * (Double.parseDouble(monthRatioByReflection) / 100);
                }
            }
            financialProgressVo.setMonthlyPlanCost(MONTHSum);

            int mont = month-i;
            List<PlanMetering> listP = planMeterings.stream().filter(f -> f.getIsSpecial().equals(String.valueOf(mont)) && f.getUnit().equals(String.valueOf(calendar.get(Calendar.YEAR)))).toList();
            if(!listP.isEmpty()){
                financialProgressVo.setMonthlyUsedCost(Double.valueOf(df.format(listP.getFirst().getSpecialUnitPrice().doubleValue())));
            }

            if(financialProgressVo.getMonthlyPlanCost() > 0){
                financialProgressVo.setMonthlyFP(df.format((financialProgressVo.getMonthlyUsedCost() / financialProgressVo.getMonthlyPlanCost()) * 100) + "%");
            }
            financialProgressVo.setAnnualPlanCost(annualPlannedExpenses);

            listRet.add(financialProgressVo);
        }

        double AccumulateUsedCost = 0;
        for (FinancialProgressVo progressVo : listRet) {
            AccumulateUsedCost += progressVo.getMonthlyUsedCost();
            progressVo.setAccumulateUsedCost(Double.valueOf(df.format(AccumulateUsedCost)));
            if (progressVo.getMonthlyPlanCost() > 0) {
                progressVo.setAnnualFP(df.format((progressVo.getAccumulateUsedCost() / progressVo.getAnnualPlanCost()) * 100) + "%");
            }
        }

        return ResultUtil.success(listRet.stream().sorted(Comparator.comparing(FinancialProgressVo::getDate).reversed()).toList());
    }






        @Operation(summary = "根据年份和工区计划费用查询")
    @GetMapping("/getPlanFeeByyearWksp")
    public Result<Map<String,Object>> getPlanFeeByyearWksp(@RequestParam("year") int year,
                                                           @RequestParam(value = "planId", required = false) String planId,
                                                           @RequestParam(value = "workspaceIds",required = false) String workspaceIds) {

        List<String> list1 = new ArrayList<>();
        if(workspaceIds != null){
            String[] split = workspaceIds.split(",");
            list1 = Arrays.asList(split);
        }

        // 获取Calendar实例
        Calendar calendar = Calendar.getInstance();
        // 获取当前月份 (注意：Calendar中的月份是从0开始的，所以需要+1)
        int month = calendar.get(Calendar.MONTH) + 1;

        MPJLambdaWrapper<PlanMetering> wrapperPM = new MPJLambdaWrapper<PlanMetering>()
                .selectAs("SUM(t.unit_price * t2.num)",PlanMetering::getSpecialUnitPrice)
                .selectAs("MONTH(t2.is_date)",PlanMetering::getIsSpecial)
                .selectAs("YEAR(t2.is_date)",PlanMetering::getUnit)
                .leftJoin(MeteringInventory.class,MeteringInventory::getId,PlanMetering::getMeteringInventoryId)
                .leftJoin(MeteringUse.class,MeteringUse::getProjectCode,MeteringInventory::getProjectNo)
                .ne(MeteringUse::getPricingType,3)
                .eq(StringUtils.isNotEmpty(planId),PlanMetering::getPlanId,planId)
                .groupBy("YEAR(t2.is_date)","MONTH(t2.is_date)");
        List<PlanMetering> planMeterings = planMeteringMapper.selectJoinList(PlanMetering.class, wrapperPM);
        double accumulatedUsageFee = planMeterings.stream().mapToDouble(m -> m.getSpecialUnitPrice().doubleValue()).sum();
        List<PlanMetering> listP = planMeterings.stream().filter(f -> f.getIsSpecial().equals(String.valueOf(month)) && f.getUnit().equals(String.valueOf(calendar.get(Calendar.YEAR)))).toList();
        double monthUsageFee = 0;
        if (!listP.isEmpty()) {
            monthUsageFee = listP.getFirst().getSpecialUnitPrice().doubleValue();
        }

        MPJLambdaWrapper<PlanMonthlyAllocation> wrapper = new MPJLambdaWrapper<PlanMonthlyAllocation>()
                .selectAll(PlanMonthlyAllocation.class)
                .leftJoin(Plan.class,Plan::getId,PlanMonthlyAllocation::getPlanId)
                .eq(Plan::getMeteringYear, year).in(!list1.isEmpty(),Plan::getWorkspaceId, list1);
        List<PlanMonthlyAllocation> list = planMonthlyAllocationService.selectJoinList(PlanMonthlyAllocation.class, wrapper);

        BigDecimal sum = list.stream()
                .map(PlanMonthlyAllocation::getOutputValue)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        double annualPlannedExpenses = sum.doubleValue() * 10000;

        double MONTHSum = 0;
        for (PlanMonthlyAllocation item : list) {
            String monthRatioByReflection = getMonthRatioByReflection(item, month);
            if(StringUtils.isNotEmpty(monthRatioByReflection)){
                double a = item.getOutputValue().doubleValue() * 10000;
                MONTHSum += a * (Double.parseDouble(monthRatioByReflection) / 100);
            }
        }

        Map<String,Object> map = new HashMap<>();
        map.put("annualPlannedExpenses",annualPlannedExpenses);//年度计划费用
        BigDecimal bd = BigDecimal.valueOf(accumulatedUsageFee);
        BigDecimal rounded = bd.setScale(2, RoundingMode.HALF_UP);
        map.put("accumulatedUsageFee",rounded.doubleValue());//累计使用费用
        map.put("monthPlannedExpenses",MONTHSum);//月计划费用
        map.put("monthUsageFee",monthUsageFee);//月使用费用

        return ResultUtil.success(map);
    }


    private static String getMonthRatioByReflection(PlanMonthlyAllocation allocation, int month) {
        try {
            String[] monthNames = {"january", "february", "march", "april", "may", "june",
                    "july", "august", "september", "october", "november", "december"};

            if (month < 1 || month > 12) {
                return "0";
            }
            String methodName = "get" + capitalize(monthNames[month-1]) + "Ratio";
            return (String) PlanMonthlyAllocation.class.getMethod(methodName).invoke(allocation);
        } catch (Exception e) {
            log.error(e.getMessage());
            return "0";
        }
    }

    private static String capitalize(String str) {
        if (str == null || str.isEmpty()) {
            return str;
        }
        return str.substring(0, 1).toUpperCase() + str.substring(1);
    }

}
